/*!
 *Esurvey.js v1.4.2
 *The MIT License (MIT)
 *Copyright © 2022 <Xing_Young>
 *Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

// 给form对象添加一个jsondata属性，用于获取json格式的表单数据
Object.defineProperty(HTMLFormElement.prototype, "jsondata", {
  get() {
    const jsondata = {};
    const formdata = new FormData(this);
    formdata.forEach((value, key) => {
      if (!jsondata[key]) {
        jsondata[key] =
          formdata.getAll(key).length > 1
            ? formdata.getAll(key)
            : formdata.get(key);
      }
    });
    return jsondata;
  },
});

function initSurvey(data, elementQuerySelect) {
  // 检查id是否唯一
  checkUniqueId(data);

  // 初始化表单认证序列
  let formValidate = [];

  // 查找表单所在根元素，并清空其子元素
  let el = document.querySelector(elementQuerySelect);
  if (!el) throw Error("can not find the element:" + elementQuerySelect);
  el.innerHTML = "";

  // 判断是否需要生成头部信息
  if (data.settings.showHead) {
    // 创建表单头部
    let surveyHeader = document.createElement("div");
    surveyHeader.setAttribute("class", "survey-header");
    surveyHeader.style.paddingBottom = "20px";
    el.appendChild(surveyHeader);

    // 创建表单标题
    let title = document.createElement("h1");
    title.innerHTML = data.title;
    title.setAttribute("class", "servey-title");
    title.style.textAlign = "center";
    title.style.paddingBottom = "20px";
    surveyHeader.appendChild(title);

    // 创建表单导语
    let introduction = document.createElement("p");
    introduction.innerHTML = data.introduction;
    introduction.setAttribute("class", "servey-introduction");
    introduction.style.textIndent = "2rem";
    surveyHeader.appendChild(introduction);

    // 添加分割线
    let line = document.createElement("hr");
    line.style.width = "100%";
    line.style.height = "3px";
    line.style.color = "#666";
    surveyHeader.appendChild(line);
  }

  // 创建表单
  let form = document.createElement("form");
  form.setAttribute("class", "survey-form");
  form.id = data.settings.formId || "esurvey-form";
  form.method = data.settings.formMethod || "POST";
  form.action = data.settings.formAction || "";
  form.target = data.settings.formTarget || "";

  el.appendChild(form);

  // 添加题目
  data.questions.forEach((item) => {
    // 创建问题盒子
    let questionBox = document.createElement("div");
    questionBox.className = "mb-4";
    questionBox.id = "survey-question-box-" + item.id;

    // 创建问题标签
    let questionLabel = document.createElement("label");
    questionLabel.setAttribute("for", "survey-question-" + item.id);
    questionLabel.className = "form-label";
    questionLabel.style.fontSize = "18px";
    questionLabel.style.lineHeight = "48px";
    questionLabel.style.fontWeight = "bolder";
    questionLabel.innerHTML = item.title;
    if (data.settings.showIndex)
      questionLabel.innerHTML = `${item.id}、${item.title}`;

    // 如果是必填则加入红色星号
    if (item.required) {
      let span = document.createElement("span");
      span.innerHTML = " [必填]";
      span.style.fontWeight = "lighter";
      span.style.color = "#FF504F";
      questionLabel.appendChild(span);
    }
    questionBox.appendChild(questionLabel);

    // 如果有问题描述则创建问题描述
    if (item.desc) {
      let questiondesc = document.createElement("div");
      questiondesc.className = "survey-question-desc mb-2";
      questiondesc.style.fontSize = "13px";
      questiondesc.style.textIndent = "2rem";
      questiondesc.style.color = "#aaa";
      questiondesc.innerText = item.desc;
      questionBox.appendChild(questiondesc);
    }

    // 创建问题主体
    if (item.type === "select") {
      addSelect(form, formValidate, questionBox, questionLabel, item);
    } else if (item.type === "radio") {
      addRadio(form, formValidate, questionBox, questionLabel, item);
    } else if (item.type === "multi-select") {
      addMultiSelect(form, formValidate, questionBox, questionLabel, item);
    } else if (item.type === "input") {
      addInput(form, formValidate, questionBox, questionLabel, item);
    } else if (item.type === "textarea") {
      addTextArea(form, formValidate, questionBox, questionLabel, item);
    } else if (item.type === "slider") {
      addSlider(form, formValidate, questionBox, questionLabel, item);
    }

    // 为题目添加错误信息提示框
    let err = document.createElement("span");
    err.id = "survey-err-" + item.id;
    err.className = "error-msg";
    err.style.color = "red";
    err.style.fontSize = "13px";
    err.style.position = "absolute";
    questionBox.appendChild(err);

    // 添加题目分割线
    let line = document.createElement("hr");
    line.style.width = "100%";
    line.style.height = "1px";
    line.style.color = "#666";
    form.appendChild(line);
  });

  // 生成按钮
  if (data.settings.submitBtn) {
    let submitBtn = document.createElement("button");
    submitBtn.className = "btn btn-outline-primary";
    submitBtn.innerHTML = data.settings.submitBtn.text || "提交表单";
    submitBtn.type = "submit";
    form.appendChild(submitBtn);
  }

  // 为表单添加自定义验证
  document.getElementById(data.settings.formId || "esurvey-form").onsubmit =
    () => {
      elemList = document.getElementsByClassName("error-msg");
      for (i = 0; i < elemList.length; i++) {
        elemList[i].innerHTML = "";
      }
      let isvalid = true;
      // 依次验证所有的validateFunc()项目
      formValidate.forEach((validateFunc) => {
        isvalid = isvalid && validateFunc();
      });
      if (!isvalid) return false;
    };
}

// 文本转问卷配置对象
function formFormatter(elementQuerySelect) {
  // 查找目标元素
  let el = document.querySelector(elementQuerySelect);
  if (!el) throw Error("can not find the element:" + elementQuerySelect);
  if (el.tagName !== "TEXTAREA")
    throw Error("formFormatter needs a <textarea> element!");

  // 构造配置对象
  let formOptions = {
    title: "",
    introduction: "",
    settings: {
      formId: null,
      formTarget: null,
      formMethod: null,
      showHead: null,
      submitBtn: null,
    },
    questions: [],
  };

  if (el.value.trim() === "") return formOptions;

  // 修改配置对象
  format(el, formOptions);

  // 将配置项返回
  return formOptions;
}

// 获取文本
function format(el, formOptions) {
  let typeHash = {
    单选: "radio",
    下拉: "select",
    多选: "multi-select",
    填空: "input",
    文本: "textarea",
    量表: "slider",
  };
  let rawData = el.value;
  let questions = rawData.split("\n\n");
  // 获取问卷标题
  formOptions.title = questions.shift();

  // 获取问卷导语
  formOptions.introduction = questions.shift();

  // 分割问题
  for (i = 0; i < questions.length; i++) {
    // 将每个问题变为一个数组
    let question = questions[i].split("\n");
    // 构造一个问题对象
    let qData = {
      id: i + 1,
      name: "question" + (i + 1).toString(),
      options: [],
    };
    // 数组的第一个元素就是问题的标题
    let qTitle = question.shift();

    // 通过正则表达式获取题目类型
    let regType = new RegExp("【.+】");
    let qType = regType.exec(qTitle);
    qType = qType ? qType[0].slice(1, -1) : "单选"; // 默认题型为单选radio题
    // 通过正则表达式匹配是否必填
    let regRequired = /@R$/;
    let qRequired = regRequired.exec(qTitle) ? true : false;
    // 配置量表题
    if (qType === "量表") {
      // 通过正则表达式匹配量表配置
      let regSlider = new RegExp("{.+}");
      let qSlider = regSlider.exec(qTitle);
      if (!qSlider) {
        qData.minText = "请配置量表";
        qData.maxText = "请配置量表";
      } else {
        qSlider = qSlider[0].slice(1, -1).split("/");
        qData.minText = qSlider[0];
        qData.min = qSlider[1];
        qData.maxText = qSlider[2];
        qData.max = qSlider[3];
        // 判断是否反向记分
        let regReverse = new RegExp("@r");
        qData.reverse = regReverse.exec(qTitle) ? true : false;
        // 判断是否隐藏分数提示
        let regHideScore = new RegExp("@h");
        qData.hideScore = regHideScore.exec(qTitle) ? true : false;
      }
    }
    // 删除题目标题中用于配置的部分
    qTitle = qTitle
      .replace(/【.*】/, "")
      .replace(/@R$/, "")
      .replace(/{.*}/, "")
      .replace(/@r/, "")
      .replace(/@h/, "");

    // 补充问题对象
    qData.title = qTitle;
    qData.type = typeHash[qType];
    qData.required = qRequired;

    // 将数组剩下的元素（即选项）添加到问题对象的options属性中
    for (j = 0; j < question.length; j++) {
      // 通过正则匹配是否有value
      let regVal = new RegExp("@[0-9]+$");
      let qVal = regVal.exec(question[j]);
      question[j] = question[j].replace(/@[0-9]*$/, "");
      qData.options.push({
        id: j + 1,
        value: qVal ? qVal[0].slice(1) : j + 1,
        label: question[j],
      });
    }
    //  将问题对象添加到表单配置项的questions中
    formOptions.questions.push(qData);
  }
}

// 防抖函数
function debounce(fn, wait) {
  let timer = null;
  return function () {
    if (timer !== null) {
      clearTimeout(timer);
    }
    timer = setTimeout(fn, wait);
  };
}

// 检查问题id是否唯一
function checkUniqueId(data) {
  let hash = {};
  data.questions.forEach((item) => {
    if (hash[item.id]) {
      throw Error(
        `A question need a unique id! But the question titled "${item.title}" has a repeated id`
      );
    }
    hash[item.id] = true;
  });
}

//#region problems
// 单选下拉题目
function addSelect(form, formValidate, questionBox, questionLabel, item) {
  let select = document.createElement("select");
  select.required = item.required;
  select.id = "survey-question-" + item.id;
  select.name = item.name || "survey-question-" + item.id;
  select.className = "form-select";
  select.setAttribute("aria-label", "survey-select-item");
  let placeholder = document.createElement("option");
  placeholder.innerHTML = "点击下拉选择";
  placeholder.setAttribute("selected", "true");
  placeholder.value = "";
  select.appendChild(placeholder);
  item.options.forEach((element) => {
    let option = document.createElement("option");
    option.innerHTML = element.label;
    option.value = element.value;
    select.appendChild(option);
  });
  questionBox.appendChild(select);
  form.appendChild(questionBox);
}

// 单选按钮
function addRadio(form, formValidate, questionBox, questionLabel, item) {
  questionBox.className = "mb-4 check-group";
  questionLabel.removeAttribute("for");

  // 创建checkList，用于排列radio
  let checkList = document.createElement("div");
  checkList.className = "check-list mx-4 row";
  item.options.forEach((element) => {
    // 创建选项radio
    let formCheck = document.createElement("div");
    formCheck.className = "form-check col-md-6 col-xxl-3 mb-3 mb-xxl-0";
    let option = document.createElement("input");
    option.className = "form-check-input";
    option.type = "radio";
    option.id = "survey-question-" + item.id + "-option-" + element.id;
    option.name = item.name || "survey-question-" + item.id;
    option.value = element.value;
    // 创建选项标签
    let checkLabel = document.createElement("label");
    checkLabel.innerHTML = element.label;
    checkLabel.className = "form-check-label";
    checkLabel.style.height = "40px";
    checkLabel.style.width = "100%";
    checkLabel.style.position = "relative";
    checkLabel.setAttribute(
      "for",
      "survey-question-" + item.id + "-option-" + element.id
    );
    formCheck.appendChild(option);
    formCheck.appendChild(checkLabel);
    checkList.appendChild(formCheck);
  });
  questionBox.appendChild(checkList);
  form.appendChild(questionBox);
  // 验证必填
  if (item.required) {
    // 在表单验证数组中添加一个验证函数validateFunc
    formValidate.push(() => {
      let options = document.getElementsByName(
        item.name || "survey-question-" + item.id
      );
      let isValid = false;
      // 只要有一项为真，则isValid为真
      options.forEach((option) => {
        isValid += option.checked;
      });
      if (!isValid) {
        document.getElementById("survey-err-" + item.id).innerText =
          "本题为必填题目！";
        window.location.href = "#survey-question-box-" + item.id;
        return false;
      }
      return true;
    });
  }
}

// 多选题目
function addMultiSelect(form, formValidate, questionBox, questionLabel, item) {
  questionBox.className = "mb-4 check-group";
  questionLabel.removeAttribute("for");

  // 创建checklist，用于排列checkbox
  let checkList = document.createElement("div");
  checkList.className = "check-list mx-4 row";
  item.options.forEach((element) => {
    // 创建选项checkbox
    let formCheck = document.createElement("div");
    formCheck.className = "form-check col-sm-6 col-md-3 mb-3 mb-md-0";
    let option = document.createElement("input");
    option.className = "form-check-input";
    option.type = "checkbox";
    option.id = "survey-question-" + item.id + "-option-" + element.id;
    option.name = item.name || "survey-question-" + item.id;
    option.value = element.value;
    // 创建选项标签
    let checkLabel = document.createElement("label");
    checkLabel.innerHTML = element.label;
    checkLabel.className = "form-check-label";
    checkLabel.setAttribute(
      "for",
      "survey-question-" + item.id + "-option-" + element.id
    );
    formCheck.appendChild(option);
    formCheck.appendChild(checkLabel);
    checkList.appendChild(formCheck);
  });
  questionBox.appendChild(checkList);
  form.appendChild(questionBox);
  // 验证必填
  if (item.required) {
    // 在表单验证数组中添加一个验证函数validateFunc
    formValidate.push(() => {
      let options = document.getElementsByName(
        item.name || "survey-question-" + item.id
      );
      let isValid = false;
      // 只要有一项为真，则isValid为真
      options.forEach((option) => {
        isValid += option.checked;
      });
      if (!isValid) {
        document.getElementById("survey-err-" + item.id).innerText =
          "本题为必填题目！";
        return false;
      }
      return true;
    });
  }
}

// 填空题
function addInput(form, formValidate, questionBox, questionLabel, item) {
  let formInput = document.createElement("input");
  formInput.required = item.required;
  formInput.id = "survey-question-" + item.id;
  formInput.name = item.name || "survey-question-" + item.id;
  formInput.className = "form-control";
  formInput.type = item.valueType || "text";
  if (item.valueType === "number") {
    formInput.setAttribute("min", item.min);
    formInput.setAttribute("max", item.max);
  } else {
    formInput.setAttribute("min-length", item.min);
    formInput.setAttribute("max-length", item.max);
  }
  formInput.placeholder = item.placeholder || "请输入" + item.title;
  questionBox.appendChild(formInput);
  form.appendChild(questionBox);
}

// 多行文本题
function addTextArea(form, formValidate, questionBox, questionLabel, item) {
  let formText = document.createElement("textarea");
  formText.required = item.required;
  formText.id = "survey-question-" + item.id;
  formText.name = item.name || "survey-question-" + item.id;
  formText.className = "form-control";
  formText.type = item.valueType || "text";
  formText.setAttribute("min-length", item.min);
  formText.setAttribute("max-length", item.max);
  formText.placeholder = item.placeholder || "请输入" + item.title;
  formText.rows = item.rows || 3;
  formText.style.resize = "unset";
  questionBox.appendChild(formText);
  form.appendChild(questionBox);
}

// 量表题
function addSlider(form, formValidate, questionBox, questionLabel, item) {
  let formSlider = document.createElement("input");
  formSlider.required = item.required;
  formSlider.id = "survey-question-" + item.id;
  formSlider.name = item.name || "survey-question-" + item.id;
  formSlider.type = "range";
  formSlider.className = "form-range";
  formSlider.value = item.min;
  formSlider.setAttribute("min", item.min);
  formSlider.setAttribute("max", item.max);
  formSlider.step = item.step || 1;
  // 构造各分值对应的文本信息
  let textHash = {};
  if (item.options && item.options.length != 0) {
    item.options.forEach((element) => {
      textHash[element.value] = element.label;
    });
  }

  // 显示当前选中值
  let tip = document.createElement("div");
  tip.className = "d-flex justify-content-between align-items-end";
  let start = document.createElement("span");
  start.innerText = item.minText + (item.hideScore ? "" : `(${item.min})`);
  start.style.fontSize = "12px";
  let end = document.createElement("span");
  end.innerText = item.maxText + (item.hideScore ? "" : `(${item.max})`);
  end.style.fontSize = "12px";
  // 如果反向记分则两边的值应该交换,初始值应当变为最大值
  if (item.reverse) {
    formSlider.style.direction = "rtl";
    start.innerText = item.maxText + (item.hideScore ? "" : `(${item.max})`);
    end.innerText = item.minText + (item.hideScore ? "" : `(${item.min})`);
    formSlider.value = item.max;
  }
  let nowVal = document.createElement("span");
  // hideScore属性仅在非李克特量表起效
  nowVal.innerText =
    (textHash[formSlider.value] ? textHash[formSlider.value] + "：" : "") +
    (item.hideScore && item.options && item.options.length != 0
      ? ""
      : item.reverse && item.hideScore
      ? parseInt(item.max) - parseInt(formSlider.value) + parseInt(item.min)
      : formSlider.value);
  nowVal.id = "survey-question-" + item.id + "-now-value";
  nowVal.style.color = "blue";
  nowVal.style.fontSize = "16px";
  tip.appendChild(start);
  tip.appendChild(nowVal);
  tip.appendChild(end);
  formSlider.oninput = function (e) {
    let label = textHash[e.target.value] ? textHash[e.target.value] + "：" : "";
    document.getElementById(
      "survey-question-" + item.id + "-now-value"
    ).innerHTML =
      label +
      (item.reverse &&
      item.hideScore &&
      (!item.options || item.options.length == 0)
        ? parseInt(item.max) - parseInt(e.target.value) + parseInt(item.min)
        : item.hideScore && item.options && item.options.length != 0
        ? ""
        : e.target.value);
  };
  questionBox.appendChild(tip);
  questionBox.appendChild(formSlider);
  form.appendChild(questionBox);
}
//#endregion problems
